

#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
#include <utility>

template <typename T>
class threadsafe_queue {
public:
    threadsafe_queue(int interval_ = 100)
        : interval(interval_)
    {
    }
    threadsafe_queue(threadsafe_queue const& other) = delete;
    threadsafe_queue& operator=(threadsafe_queue const&) = delete;
    threadsafe_queue& operator=(threadsafe_queue&& other) = delete;
    threadsafe_queue(threadsafe_queue&& other) = delete;
    ~threadsafe_queue() = default;

    void push(T new_value)
    {
        std::lock_guard<std::mutex> lock(m);
        data.push(std::move(new_value));
        cv.notify_one();
    }

    template <typename... Args>
    void emplace(Args&&... args)
    {
        std::lock_guard<std::mutex> lock(m);
        data.emplace(std::forward<Args>(args)...);
        cv.notify_one();
    }

    bool wait_and_pop(T& value)
    {
        std::unique_lock<std::mutex> lock(m);
        cv.wait(lock, [this]() { return !data.empty(); });
        value = std::move(data.front());
        data.pop();
        return true;
    }

    std::shared_ptr<T> wait_and_pop()
    {
        std::unique_lock<std::mutex> lock(m);
        cv.wait(lock, [this]() { return !data.empty(); });
        auto res = std::make_shared<T>(std::move(data.front()));
        data.pop();
        return res;
    }

    bool wait_for_and_pop(T& value)
    {
        std::unique_lock<std::mutex> lock(m);
        if (!cv.wait_for(lock, interval, [this]() { return !data.empty(); })) {
            return false;
        }
        value = std::move(data.front());
        data.pop();
        return true;
    }

    std::shared_ptr<T> wait_for_and_pop()
    {
        std::unique_lock<std::mutex> lock(m);
        if (!cv.wait_for(lock, interval, [this]() { return !data.empty(); })) {
            return nullptr;
        }
        auto res = std::make_shared<T>(std::move(data.front()));
        data.pop();
        return res;
    }

    bool try_pop(T& value)
    {
        std::lock_guard<std::mutex> lock(m);
        if (data.empty()) {
            return false;
        }
        value = std::move(data.front());
        data.pop();
        return true;
    }

    std::shared_ptr<T> try_pop()
    {
        std::lock_guard<std::mutex> lock(m);
        if (data.empty()) {
            return nullptr;
        }
        auto res = std::make_shared<T>(std::move(data.front()));
        data.pop();
        return res;
    }

    bool empty() const
    {
        std::lock_guard<std::mutex> lock(m);
        return data.empty();
    }

private:
    mutable std::mutex m;
    std::queue<T> data;
    std::condition_variable cv;
    std::chrono::milliseconds interval;
};